home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / c / library / dos / communic / pcmail / daemon / pc-maild.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-05  |  15.0 KB  |  568 lines

  1. /*++
  2. /* NAME
  3. /*    pc-maild 8
  4. /* SUMMARY
  5. /*    deliver unsent mail
  6. /* PROJECT
  7. /*    pc-mail
  8. /* PACKAGE
  9. /*    nfs
  10. /* SYNOPSIS
  11. /*    pc-maild [delay]
  12. /* DESCRIPTION
  13. /*    This program should be run on the nfs file server that exports
  14. /*    mail directories to MS-DOS pc-mail users. It replaces the
  15. /*    (MS-DOS -> UNIX) transmission function of the MS-DOS \fIcico\fR
  16. /*    program.
  17. /*
  18. /*    The per-user mail directories (default: /usr/spool/pc-mail/\fIuser\fR)
  19. /*    are scanned for outgoing mail every \fIdelay\fR seconds (default: 300).
  20. /*    When outgoing mail is found, it is sent through the UNIX rmail program
  21. /*    (uucp mail interface) and the corresponding files are removed from the
  22. /*    user\'s mail directory.
  23. /*
  24. /*    The program should run with root privileges. It will assume
  25. /*    the (uid, gid) of the sending user before accessing mail files.
  26. /* COMMANDS
  27. /*    rmail(1), uucp mail interface program
  28. /* FILES
  29. /*    /usr/spool/pc-mail/\fIuser\fR/dNNNNN, mail message (unsent mail)
  30. /*    /usr/spool/pc-mail/\fIuser\fR/xNNNNN, recipients, subject (unsent mail)
  31. /*    /usr/spool/pc-mail/\fIuser\fR/qNNNNN, mail message (sent mail)
  32. /*    /usr/spool/pc-mail/\fIuser\fR/rNNNNN, recipients, subject (sent mail)
  33. /*    (NNNNN is the pc-mail "message id").
  34. /* SEE ALSO
  35. /*    pc-mail(1)
  36. /* DIAGNOSTICS
  37. /*    Errors found during initialization cause the program to
  38. /*    terminate with a diagnostic on the standard error stream.
  39. /*    All other errors are considered transient, i.e. if something
  40. /*    fails, it is tried again at a later time.  Where possible,
  41. /*    diagnostics are logged through the syslog facility.
  42. /* BUGS
  43. /*    Scanning mail directories is an inefficient way to detect
  44. /*    unsent mail.
  45. /* AUTHOR(S)
  46. /*    W.Z. Venema
  47. /*    Eindhoven University of Technology
  48. /*    Department of Mathematics and Computer Science
  49. /*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  50. /* CREATION DATE
  51. /*    Sun Oct 22 22:12:15 MED 1989
  52. /* LAST MODIFICATION
  53. /*    1/6/90 19:45:05
  54. /* VERSION/RELEASE
  55. /*    1.6
  56. /*--*/
  57.  
  58. #ifndef lint
  59. static char sccsid[] = "@(#) pc-maild.c 1.6 1/6/90 19:45:05";
  60.  
  61. #endif
  62.  
  63.  /*
  64.   * General return-value conventions:
  65.   * 
  66.   * int func():        0 means OK
  67.   * 
  68.   * stuff *func():    0 means error
  69.   * 
  70.   */
  71.  
  72. #include <stdio.h>
  73. #include <pwd.h>
  74. #include <time.h>
  75. #include <signal.h>
  76. #include <sys/types.h>
  77. #include <sys/stat.h>
  78.  
  79. #ifdef SYSLOG
  80. #include <syslog.h>
  81. #else
  82. #include "syslog.h"
  83. #endif
  84.  
  85. #ifdef SYSV
  86. #include <sys/utsname.h>
  87. #include <ndir.h>
  88. #else
  89. #include <sys/types.h>
  90. #include <sys/dir.h>
  91. #include <sgtty.h>
  92. #endif
  93.  
  94. #include "dosunix.h"
  95. #include "util.h"
  96. #include "mtime.h"
  97.  
  98. /* Library functions */
  99.  
  100. extern char *strtok();
  101. extern char *strncpy();
  102. extern struct passwd *getpwnam();
  103. extern unsigned sleep();
  104. extern void exit();
  105. extern void _exit();
  106. extern struct tm *localtime();
  107. extern char *asctime();
  108. extern long time();
  109.  
  110. /* Local defines, declarations */
  111.  
  112. #ifndef    DELAY
  113. #define    DELAY    300            /* default: scan every 5 minutes */
  114. #endif
  115.  
  116. #ifndef    MAILDIR
  117. #define    MAILDIR    "/usr/spool/pc-mail"    /* default pc-mail directory tree */
  118. #endif
  119.  
  120. #define    MAXLINE    1024            /* max length of recipients line */
  121.  
  122. void    catchsig();
  123. void    finduser();
  124. char   *getrcpt();
  125. char   *fullname();
  126.  
  127. int     delay = DELAY;            /* the default delay */
  128. char   *progname;            /* my process name */
  129.  
  130.  
  131. int     main(argc, argv)
  132. int     argc;
  133. char  **argv;
  134. {
  135.     progname = *argv;
  136.  
  137.     /* Sanity checks */
  138.  
  139. #ifndef DEBUG
  140.  
  141.     if (geteuid() != 0) {
  142.     (void) fprintf(stderr, "%s: must be run as root\n", progname);
  143.     exit(1);
  144.     }
  145. #endif
  146.  
  147.     /* Check for command-line delay argument */
  148.  
  149.     if (argc > 1 && (delay = atoi(argv[1])) < 1)
  150.     delay = DELAY;
  151.  
  152.     /* Become a daemon process */
  153.  
  154. #ifndef DEBUG
  155.     disconnect();
  156. #endif
  157.  
  158.     /* Set up signal handling */
  159.  
  160.     catchsig();
  161.  
  162.     /* Enable syslogging */
  163.  
  164.     (void) openlog(progname, LOG_PID, LOG_MAIL);
  165.     syslog(LOG_WARNING, "daemon restarted");
  166.  
  167.     /* Setup a decent environment */
  168.  
  169.     if (putenv("PATH=/bin:/usr/bin:/usr/ucb") || putenv("IFS= \t\n")) {
  170.     syslog(LOG_WARNING, "initialization failed (insufficient resources)");
  171.     exit(1);
  172.     }
  173.     /* Enter the main loop */
  174.  
  175.     finduser();
  176.     /* NOTREACHED */
  177. }
  178.  
  179. /* finduser - repeatedly iterate over all pc-mail user directories */
  180.  
  181. void    finduser()
  182. {
  183.     register DIR *maildp;
  184.     register struct direct *dp;
  185.     register struct passwd *uinfo;
  186.     MTIME  *dirtime;
  187.     char    userdir[BUFSIZ];
  188.     struct stat st;
  189.  
  190.     /*
  191.      * Ignore names that start with a period.
  192.      *
  193.      * Ignore names that do not correspond to a directory.
  194.      * 
  195.      * Ignore directories that did not change since the last time they were
  196.      * known to hold no unsent mail.
  197.      * 
  198.      * Ignore directories that do not have a name equal to the login name of a
  199.      * user.
  200.      */
  201.  
  202.     for (;;) {
  203.     if ((e_chdir(MAILDIR) == 0) && (maildp = e_opendir(MAILDIR))) {
  204.         while (dp = readdir(maildp)) {
  205.         if ((dp->d_name[0] != '.')
  206.         && (stat(dp->d_name, &st) == 0)
  207.         && ((st.st_mode & S_IFMT) == S_IFDIR)
  208.         && (st.st_mtime > (dirtime = mtime(dp->d_name))->time)
  209.         && ((uinfo = getpwnam(dp->d_name)) != 0)) {
  210.             (void) sprintf(userdir, "%s/%s", MAILDIR, dp->d_name);
  211.             if (findmail(uinfo, userdir) == 0)
  212.             dirtime->time = st.st_mtime;    /* say it was empty */
  213.         }
  214.         }
  215.         closedir(maildp);            /* done with this user */
  216.         (void) chdir("/");            /* be friendly */
  217.     }
  218.     (void) sleep((unsigned) delay);        /* try again later */
  219.     }
  220. }
  221. /* findmail - enter a user\'s mail directory and scan for unsent mail */
  222.  
  223. int     findmail(uinfo, userdir)
  224. struct passwd *uinfo;
  225. char   *userdir;
  226. {
  227.     register DIR *dd;
  228.     register struct direct *p;
  229.     int     seqno;
  230.     static char xfile[BUFSIZ];        /* file with recipients (unsent mail) */
  231.     static char rfile[BUFSIZ];        /* same, but with "Sent" status */
  232.     static char dfile[BUFSIZ];        /* file with message (unsent mail) */
  233.     static char qfile[BUFSIZ];        /* same, but with "Sent" status */
  234.     int     found = 0;            /* no mail found yet */
  235.  
  236.     /*
  237.      * Use the fact that 'x' files (recipient addresses) are created later
  238.      * than 'd' files (message body) when a pc user generates a message.
  239.      * Extract the pc-mail message id from the file name and try to pipe the
  240.      * message through the UNIX rmail command. All knowledge about pc-mail
  241.      * file names resides in this function. Return zero if no unsent mail was
  242.      * found.
  243.      */
  244.  
  245.     if ((e_chdir(userdir) == 0) && (dd = e_opendir(userdir))) {
  246.     while (p = readdir(dd)) {
  247.         if (*p->d_name == 'x' && sscanf(p->d_name + 1, "%d", &seqno) == 1) {
  248.         (void) sprintf(xfile, "x%05d", seqno);    /* recipients */
  249.         if (strcmp(p->d_name, xfile) == 0) {    /* ignore junk */
  250.             (void) sprintf(dfile, "d%05d", seqno);
  251.             (void) sprintf(rfile, "r%05d", seqno);
  252.             (void) sprintf(qfile, "q%05d", seqno);
  253.             pickup(uinfo, xfile, dfile, rfile, qfile);
  254.             found = 1;            /* found unsent mail */
  255.         }
  256.         }
  257.     }
  258.     closedir(dd);
  259.     (void) chdir(MAILDIR);
  260.     }
  261.     return (found);
  262. }
  263.  
  264. /* pickup - pick up one message from a user\'s mail directory */
  265.  
  266. pickup(uinfo, xfile, dfile, rfile, qfile)
  267. struct passwd *uinfo;
  268. char   *xfile;
  269. char   *dfile;
  270. char   *rfile;
  271. char   *qfile;
  272. {
  273.  
  274.     /*
  275.      * Actual delivery must be done with the (uid, gid) of the sender, or the
  276.      * From: lines will not be correct. Therefore, we do delivery in a child
  277.      * process. This also avoid all kinds of nasty security problems. All
  278.      * errors are considered non-fatal.
  279.      */
  280.  
  281. #ifdef DEBUG
  282.     sendasuser(uinfo, xfile, dfile, rfile, qfile);    /* don't fork */
  283. #else
  284.     switch (e_fork()) {
  285.     case -1:                    /* failure */
  286.     break;
  287.     case 0:                    /* child */
  288.     sendasuser(uinfo, xfile, dfile, rfile, qfile);
  289.     _exit(0);
  290.     /* NOTREACHED */
  291.     default:                    /* parent */
  292.     (void) wait((int *) 0);
  293.     break;
  294.     }
  295. #endif
  296. }
  297.  
  298. /* sendasuser - send mail through rmail(1), having (uid, gid) of sender */
  299.  
  300. sendasuser(uinfo, xfile, dfile, rfile, qfile)
  301. struct passwd *uinfo;
  302. char   *xfile;
  303. char   *dfile;
  304. char   *rfile;
  305. char   *qfile;
  306. {
  307.     char   *dest;            /* recipient address(es) */
  308.  
  309.     /*
  310.      * User-specific mail files must be opened AFTER we have assumed the
  311.      * (uid, gid) of the pc-mail user; this in order to avoid nasty security
  312.      * holes. When delivery succeeds, we rename the spool files so they
  313.      * reflect the "Sent" status.
  314.      */
  315.  
  316.     if ((setugid(uinfo) == 0)            /* assume proper (uid, gid) */
  317.     && (dest = getrcpt(uinfo, xfile))        /* extract recipients */
  318.     && (rmail(uinfo, dfile, dest) == 0)) {    /* pipe message through rmail */
  319.     (void) unlink(rfile);            /* just in case */
  320.     (void) unlink(qfile);            /* just in case */
  321.     (void) u_link(uinfo, xfile, rfile);    /* change status to "Sent" */
  322.     (void) u_link(uinfo, dfile, qfile);    /* change status to "Sent" */
  323.     (void) u_unlink(uinfo, xfile);        /* recipients file */
  324.     (void) u_unlink(uinfo, dfile);        /* message body file */
  325.     }
  326. }
  327.  
  328. /* setugid - assume (uid, gid) of user */
  329.  
  330. int     setugid(uinfo)
  331. struct passwd *uinfo;
  332. {
  333.     if (setgid(uinfo->pw_gid)) {
  334.     syslog(LOG_WARNING, "setgid(%s) failed: %m", uinfo->pw_name);
  335.     return (1);
  336.     }
  337.     if (setuid(uinfo->pw_uid)) {
  338.     syslog(LOG_WARNING, "setuid(%s) failed: %m", uinfo->pw_name);
  339.     return (1);
  340.     } else {
  341.     return (0);
  342.     }
  343. }
  344.  
  345. /* getrcpt - extract recipient from user mail file */
  346.  
  347. char   *getrcpt(uinfo, xfile)
  348. struct passwd *uinfo;
  349. char   *xfile;
  350. {
  351.     FILE   *xfp;            /* recipient file pointer */
  352.     static char dest[MAXLINE];        /* recipient names */
  353.     register char *ret;
  354.  
  355.     if ((xfp = u_fopen(uinfo, xfile, "r")) == 0) {
  356.     return (0);
  357.     } else {
  358.     pc_wait(fileno(xfp));        /* let pc finish writing */
  359.     ret = dosgets(dest, sizeof(dest), xfp);
  360.     (void) fclose(xfp);
  361.     if (ret == 0)
  362.         syslog(LOG_WARNING, "no recipients specified in %s/%s",
  363.            uinfo->pw_name, xfile);
  364.     return (ret);
  365.     }
  366. }
  367.  
  368. /* rmail - pipe a pc mail message through the UNIX rmail program */
  369.  
  370. int     rmail(uinfo, dfile, dest)
  371. struct passwd *uinfo;            /* originator */
  372. char   *dfile;                /* message file */
  373. char   *dest;                /* recipients */
  374. {
  375.     static char cmd[MAXLINE+20];    /* command + arguments */
  376.     FILE   *dfp;            /* source stream */
  377.     FILE   *pfp;            /* output stream */
  378.     int     ret;            /* return value, 0 if OK */
  379.     long    secs;            /* absolute UNIX time */
  380.     static char hostname[BUFSIZ];    /* our host name */
  381.  
  382.     /*
  383.      * The UNIX rmail command needs a UUCP-style From_ line.
  384.      * 
  385.      * The To: and From: lines can be added for esthetical porposes.
  386.      * 
  387.      * Report communication failures with the rmail command. Error returns from
  388.      * rmail are ignored; they should be handled in sendmail.
  389.      */
  390.  
  391.     if (dfp = u_fopen(uinfo, dfile, "r")) {    /* open message file */
  392.     (void) sprintf(cmd, "rmail %s", dest);
  393.     if ((pfp = popen(cmd, "w")) == 0) {    /* invoke rmail... */
  394.         syslog(LOG_WARNING, "cannot invoke %.20s...: %m", rmail);
  395.         ret = 1;
  396.     } else {
  397.         secs = time((long *) 0);
  398.         (void) gethostname(hostname, sizeof(hostname));
  399.         (void) fprintf(pfp, "From %s %.24s remote from %s\n", 
  400.                uinfo->pw_name,
  401.                asctime(localtime(&secs)),
  402.                hostname);        /* add UUCP From_ line */
  403. #ifdef    RFC822
  404.         rfc822hdr(uinfo, dest, pfp);    /* do RFC822 stuff */
  405. #endif
  406.         if (ret = dos2unix(dfp, pfp))    /* append message body */
  407.         syslog(LOG_WARNING, "write to rmail failed: %m");
  408.         (void) pclose(pfp);
  409.     }
  410.     (void) fclose(dfp);
  411.     }
  412.     return (ret);
  413. }
  414.  
  415. /* rfc822hdr - generate subset of RFC822 header lines */
  416.  
  417. rfc822hdr(uinfo, dest, pfp)
  418. register struct passwd *uinfo;
  419. char   *dest;
  420. register FILE *pfp;
  421. {
  422.     char   *sep = " ,\t\r\n";
  423.     char   *name;
  424.     int     n = 0;
  425.  
  426.     /*
  427.      * There are a few problems with this function. First of all, it destroys
  428.      * the dest argument. In the second place, putting each recipient on a
  429.      * separate header line is ugly; fortunately, sendmail will fix this.
  430.      */
  431.  
  432.     (void) fprintf(pfp, "From: %s (%s)\n", uinfo->pw_name,
  433.         fullname(uinfo));            /* add From: header line */
  434.     for (name = strtok(dest, sep); name; name = strtok((char *) 0, sep))
  435.     (void) fprintf(pfp, "%s%s", n == 0 ? "To: " : ",\n    ", name);
  436.     if (n)
  437.     (void) putc('\n', pfp);
  438. }
  439.  
  440. /* fullname - extract full name from gecos field */
  441.  
  442. char   *fullname(uinfo)
  443. struct passwd *uinfo;
  444. {
  445.     static char name[BUFSIZ];
  446.  
  447.     /* This code assumes BSD-style gecos fields (name,stuff,stuff...) */
  448.  
  449.     if (sscanf(uinfo->pw_gecos, "%[^,]", name) == 0)
  450.     name[0] = '\0';
  451.     return (name);
  452. }
  453.  
  454. /* gotsig - caught a signal; terminate with diagnostic */
  455.  
  456. void    gotsig(sig)
  457. int     sig;
  458. {
  459.     syslog(LOG_WARNING, "going down on signal %d", sig);
  460.     closelog();
  461.     exit(sig);
  462. }
  463.  
  464. /* catchsig - catch some signals */
  465.  
  466. void    catchsig()
  467. {
  468. #ifdef    DEBUG
  469.     (void) signal(SIGHUP, gotsig);
  470.     (void) signal(SIGINT, gotsig);
  471.     (void) signal(SIGQUIT, gotsig);
  472. #else
  473.     (void) signal(SIGHUP, SIG_IGN);
  474.     (void) signal(SIGINT, SIG_IGN);
  475.     (void) signal(SIGQUIT, SIG_IGN);
  476. #endif
  477.     (void) signal(SIGBUS, gotsig);
  478.     (void) signal(SIGSEGV, gotsig);
  479.     (void) signal(SIGTERM, gotsig);
  480. }
  481.  
  482. /* disconnect - become a daemon process */
  483.  
  484. disconnect()
  485. {
  486. #ifndef    SYSV
  487.     int     fd;
  488.  
  489. #endif
  490.  
  491.     /* Get rid of the parent process */
  492.  
  493.     switch (e_fork()) {
  494.     case -1:                    /* failure */
  495.     exit(1);
  496.     /* NOTREACHED */
  497.     default:
  498.     _exit(0);                /* parent */
  499.     /* NOTREACHED */
  500.     case 0:                    /* child */
  501.     break;
  502.     }
  503.  
  504.     /* Get rid of the controlling terminal */
  505.  
  506.     (void) close(0);
  507.     (void) close(1);
  508.     (void) close(2);
  509. #ifdef SYSV
  510.     (void) setpgrp();
  511. #else
  512.     if ((fd = open("/dev/tty", 0)) >= 0) {
  513.     (void) ioctl(fd, TIOCNOTTY, 0);
  514.     (void) close(fd);
  515.     }
  516. #endif
  517. }
  518.  
  519. /* pc_wait - wait till the pc has finished writing a file */
  520.  
  521. pc_wait(fd)
  522. int     fd;
  523. {
  524.     struct stat st;
  525.     long    oldsize = 0;
  526.  
  527.     /*
  528.      * Repeatedly sleep one second until the file size does not change
  529.      * anymore.
  530.      * 
  531.      * At first sight, this does not seem to be a very robust algorithm. It is,
  532.      * however, sufficient. The pc sofware will first create a message file,
  533.      * then the file with recipient addresses. The pc-maild program, on the
  534.      * other hand, will read the recipient-address file first. If that file
  535.      * turns out to be empty, it will try again at a later time. So, the only
  536.      * time we may produce an incorrect result is under the following
  537.      * conditions:
  538.      * 
  539.      * (1) the file with recipient names is longer than the PC/NFS packet size
  540.      * or the pc\'s stdio buffer size.
  541.      * 
  542.      * (2) the network connection goes down for > 1 second after part of the
  543.      * data has arrived in the file with recipient addresses.
  544.      */
  545.  
  546.     while (fstat(fd, &st) == 0 && oldsize != st.st_size) {
  547.     oldsize = st.st_size;
  548.     (void) sleep(1);
  549.     }
  550. }
  551.  
  552. #ifdef SYSV
  553.  
  554. /* gethostname - BSD compatibility routine */
  555.  
  556. gethostname(name, len)
  557. char   *name;
  558. int     len;
  559. {
  560.     struct utsname ut;
  561.  
  562.     (void) uname(&ut);
  563.     (void) strncpy(name, ut.nodename, len);
  564.     return (0);
  565. }
  566.  
  567. #endif
  568.